#pragma once

#define _DEBUG

// Version values must not exceed 0xFF.
#define VERSION_MAJOR       1
#define VERSION_MINOR       3
#define VERSION_REVISION    0
#define VERSION_BUILD       1

#define XLLN_LOG_LEVEL_MASK		0b00111111
// Function call tracing.
#define XLLN_LOG_LEVEL_TRACE	0b00000001
// Function, variable and operation logging.
#define XLLN_LOG_LEVEL_DEBUG	0b00000010
// Generally useful information to log (service start/stop, configuration assumptions, etc).
#define XLLN_LOG_LEVEL_INFO		0b00000100
// Anything that can potentially cause application oddities, but is being handled adequately.
#define XLLN_LOG_LEVEL_WARN		0b00001000
// Any error which is fatal to the operation, but not the service or application (can't open a required file, missing data, etc.).
#define XLLN_LOG_LEVEL_ERROR	0b00010000
// Errors that will terminate the application.
#define XLLN_LOG_LEVEL_FATAL	0b00100000

#define XLLN_LOG_CONTEXT_MASK			(0b10000111 << 8)
// Logs related to Xlive functionality.
#define XLLN_LOG_CONTEXT_XLIVE			(0b00000001 << 8)
// Logs related to XLiveLessNess functionality.
#define XLLN_LOG_CONTEXT_XLIVELESSNESS	(0b00000010 << 8)
// Logs related to XLLN-Module functionality.
#define XLLN_LOG_CONTEXT_XLLN_MODULE	(0b00000100 << 8)
// Logs related to functionality from other areas of the application.
#define XLLN_LOG_CONTEXT_OTHER			(0b10000000 << 8)

#define IsUsingBasePort(base_port) (base_port != 0 && base_port != 0xFFFF)

uint32_t XLLNDebugLogF(uint32_t logLevel, const char *const format, ...);

#ifdef _DEBUG
#define XLLN_DEBUG_LOG(logLevel, format, ...) XLLNDebugLogF(logLevel, format, ## __VA_ARGS__)
#define GET_SOCKADDR_INFO(sockAddrStorage) GetSockAddrInfo(sockAddrStorage)
#else
#define XLLN_DEBUG_LOG(logLevel, format, ...)
#define GET_SOCKADDR_INFO(sockAddrStorage) NULL
#endif

namespace XLLNNetPacketType {
	const char* const TYPE_NAMES[] {
		"UNKNOWN",
		"TITLE_PACKET",
		"TITLE_BROADCAST_PACKET",
		"PACKET_FORWARDED",
		"UNKNOWN_USER_ASK",
		"UNKNOWN_USER_REPLY",
		"CUSTOM_OTHER",
		"LIVE_OVER_LAN_ADVERTISE",
		"LIVE_OVER_LAN_UNADVERTISE",
		"HUB_REQUEST",
		"HUB_REPLY",
		"QOS_REQUEST",
		"QOS_RESPONSE",
		"HUB_OUT_OF_BAND",
		"HUB_RELAY",
	};
	typedef enum : uint8_t {
		tUNKNOWN = 0,
		tTITLE_PACKET,
		tTITLE_BROADCAST_PACKET,
		tPACKET_FORWARDED,
		tUNKNOWN_USER_ASK,
		tUNKNOWN_USER_REPLY,
		tCUSTOM_OTHER,
		tLIVE_OVER_LAN_ADVERTISE,
		tLIVE_OVER_LAN_UNADVERTISE,
		tHUB_REQUEST,
		tHUB_REPLY,
		tQOS_REQUEST,
		tQOS_RESPONSE,
		tHUB_OUT_OF_BAND,
		tHUB_RELAY,
	} TYPE;

#pragma pack(push, 1) // Save then set byte alignment setting.

	typedef struct {
		uint32_t instanceId = 0; // a generated UUID for that instance.
		uint16_t portBaseHBO = 0; // Base Port of the instance. Host Byte Order.
		uint16_t socketInternalPortHBO = 0;
		int16_t socketInternalPortOffsetHBO = 0;
		uint32_t instanceIdConsumeRemaining = 0; // the instanceId that should be consuming the rest of the data on this packet.
	} NET_USER_PACKET;
	
	typedef struct {
		sockaddr_storage originSockAddr;
		NET_USER_PACKET netter;
		// The data following this is the forwarded packet data.
	} PACKET_FORWARDED;
	
	typedef struct {
		NET_USER_PACKET netter;
		// The data following this is the forwarded packet data.
	} UNKNOWN_USER;
	
	typedef struct {
		uint32_t xllnVersion = 0; // version of the requester.
		uint32_t instanceId = 0; // Instance ID of the requester.
		uint32_t titleId = 0;
		uint32_t titleVersion = 0;
		uint32_t portBaseHBO = 0;
	} HUB_REQUEST_PACKET;
	
	typedef struct {
		uint8_t isHubServer = 0; // boolean.
		uint32_t xllnVersion = 0; // version of the replier.
		uint32_t recommendedInstanceId = 0; // the Instance ID that should be used instead (in case of collisions).
	} HUB_REPLY_PACKET;
	
	typedef struct {
		uint8_t sessionType = 0;
		uint64_t xuid = 0;
	} LIVE_OVER_LAN_UNADVERTISE;
	
	typedef struct {
		uint32_t qosLookupId = 0;
		uint64_t sessionId = 0; // XNKID
		uint32_t probeId = 0;
		uint32_t instanceId = 0; // Instance ID of the requester.
	} QOS_REQUEST;
	
	typedef struct {
		uint32_t qosLookupId = 0;
		uint64_t sessionId = 0; // XNKID
		uint32_t probeId = 0;
		uint32_t instanceId = 0; // Instance ID of the responder.
		uint8_t enabled = 0;
		uint16_t sizeData = 0; // the amount of data appended to the end of this packet type.
	} QOS_RESPONSE;
	
	typedef struct {
		uint32_t instanceId = 0;
		uint8_t portOffsetHBO = 0xFF;
		uint16_t portOriginalHBO = 0;
	} HUB_OUT_OF_BAND;
	
	typedef struct {
		sockaddr_storage destSockAddr;
		NET_USER_PACKET netterOrigin;
		// The data following this is the packet data.
	} HUB_RELAY;

#pragma pack(pop) // Return to original alignment setting.

}
